﻿using System;
using System.Linq;
using NUnit.Framework;
using NumSkull;

namespace NumSkullTests
{
    [TestFixture]
    [Category("Unit")]
    public class HistogramTests
    {
        [Test]
        public void DefaultConstructs()
        {
            new Histogram();
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ThrowsWhenBoundsIsNull()
        {
            new Histogram(null, 0);
        }

        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void ThrowsWhenBinsIsZero()
        {
            new Histogram(new Bounds(0, 1), 0);
        }

        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void ThrowsWhenBinsIsNegative()
        {
            new Histogram(new Bounds(0, 1), -1);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ThrowsWhenBinsIsNull()
        {
            new Histogram(null);
        }

        [Test]
        public void ConstructsWithBoundsAndBins()
        {
            new Histogram(new Bounds(1d, 10d), 5);
        }

        [Test]
        public void ConstructsWithBins()
        {
            new Histogram(new[] { 0, 1, 2, 3, 4, 5, 6d });
        }

        [Test]
        public void DefaultsToEvenBins()
        {
            var histogram = new Histogram(new Bounds(1d, 10d), 5);
            Assert.That(histogram.Bins.Count, Is.EqualTo(5));
        }

        [Test]
        public void ConstructsWithSquareRootBins()
        {
            var histogram = new Histogram(new Bounds(1d, 10d), 5, BinCalculationMethod.SquareRoot);
            Assert.That(histogram.Bins.Count, Is.EqualTo(5));
        }

        [Test]
        public void ReturnsBinFromIndex()
        {
            var histogram = new Histogram(new[] { 0, 1, 2, 3, 4, 5, 6d });
            var bin = histogram.Bins[4];
            
            Assert.That(bin.Bounds, Is.EqualTo(new Bounds(4, 5)));
        }

        [Test]
        public void ClearsBins()
        {
            var histogram = new Histogram(new Bounds(1d, 10d), 5);
            histogram.Clear();

            Assert.That(histogram.Bins.Count, Is.EqualTo(0));
        }

        [Test]
        public void ClearsBounds()
        {
            var histogram = new Histogram(new Bounds(1d, 10d), 5);
            histogram.Clear();

            Assert.That(histogram.Bounds, Is.SameAs(Bounds.Empty));
        }

        [Test]
        public void BinsSingleValue()
        {
            var histogram = new Histogram(new Bounds(1, 100), 10);

            histogram.Add(45);

            Assert.That(histogram.Bins[4].Values.Contains(new HistogramBinValue(45)), Is.True);
        }

        [Test]
        public void BinsRangedValues()
        {
            var histogram = new Histogram(new Bounds(1, 100), 10);

            histogram.Add(new[] {8, 21, 34d });

            Assert.That(histogram.Bins[0].Values.Contains(new HistogramBinValue(8d)), Is.True);
            Assert.That(histogram.Bins[2].Values.Contains(new HistogramBinValue(21d)), Is.True);
            Assert.That(histogram.Bins[3].Values.Contains(new HistogramBinValue(34d)), Is.True);
        }

        [Test]
        public void FindsBinForValue()
        {
            var histogram = new Histogram(new Bounds(1, 100), 10);

            var bin = histogram.Find(45);

            Assert.That(bin.Bounds.IsInRange(45), Is.True);
        }

        [Test]
        public void FindsBinForValueBeforeValueIsAdded()
        {
            var histogram = new Histogram(new Bounds(1, 100), 10);

            var bin = histogram.Find(45);

            Assert.That(bin.Values, Is.Not.Contains(45));
        }

        [Test]
        public void FindsBinWithValue()
        {
            var histogram = new Histogram(new Bounds(1, 100), 10);
            histogram.Add(45);
            
            var bin = histogram.Find(45);

            Assert.That(bin.Values, Contains.Item(new HistogramBinValue(45)));
        }

        [Test]
        public void GetsBinTotals()
        {
            var histogram = new Histogram(new Bounds(1, 100), 10);
            histogram.Add(new[] { 1, 1, 1, 12, 12, 12, 13, 14, 17, 32, 33, 34, 33, 34d });

            var totals = histogram.GetBinTotals().ToList();

            Assert.That(totals, Is.EquivalentTo(new[] { 3, 6, 0, 5, 0, 0, 0, 0, 0, 0 }));
        }
    }
}
